export default class Signal<T = () => void | void> {
    private _cbs: any[][] = [];

    constructor() {
        this.trigger = <any>this.triggerInner;
    }

    public clear(){
        this._cbs.length = 0;
    }

    public connect(func: T, thisObject: any) {
        for (const info of this._cbs) {
            if (info[0] == func && info[1] == thisObject) {
                return;
            }
        }
        this._cbs.push([func, thisObject]);

        if (thisObject instanceof cc.Component) {
            if (!thisObject['__disconnectFuncs']) {
                thisObject['__disconnectFuncs'] = [];

                const oldDestroy = thisObject['onDestroy'];

                thisObject['onDestroy'] = function() {
                    for (const func of thisObject['__disconnectFuncs']) {
                        func();
                    }
                    if (oldDestroy) {
                        oldDestroy.call(this);
                    }
                };
            }
            thisObject['__disconnectFuncs'].push(() => {
                this.disconnect(func, thisObject);
            });
        }
    }

    public disconnect(func: T, thisObject: any) {
        const cbs = this._cbs;

        for (let i = 0, len = cbs.length; i < len; ++i) {
            const info = cbs[i];
            if (info[0] == func && info[1] == thisObject) {
                cbs.splice(i, 1);
                break;
            }
        }
    }

    public trigger: T;

    private triggerInner(...args: any[]) {
        if (this._cbs.length > 0) {
            const copyCbs = this._cbs.slice();

            for (const info of copyCbs) {
                try{
                    info[0].call(info[1], ...args);
                }catch(error){
                    console.error(error);
                }
            }
        }
    }
}